"""
任务管理API路由
"""
from typing import Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query, UploadFile, File, Body
from fastapi.responses import StreamingResponse
from motor.motor_asyncio import AsyncIOMotorDatabase
from bson import ObjectId
from pymongo import ASCENDING, DESCENDING
from datetime import datetime
import io
from urllib.parse import quote
from openpyxl import Workbook, load_workbook
from openpyxl.styles import Font, PatternFill, Alignment
from app.core.config import settings

from app.api.deps import get_db, get_current_user
from app.services.permission_service import PermissionService
from app.services.task_service import TaskService
from app.schemas.task import (
    TaskCreate,
    TaskUpdate,
    TaskStatusUpdate,
    TaskAssigneeUpdate
)
from app.models.task import (
    TaskTimeLog
)
from app.core.permissions import Permissions
from app.core.response import success_response, error_response, paginated_response, ErrorTypes, ApiResponse
from app.core.state_validator import get_state_validator
from app.utils.task_utils import format_task_response
from app.services.attachment_service import AttachmentService
from app.services.notification_service import NotificationService
from app.models.notification import NotificationCreate, NotificationType, RelatedObjectType
import asyncio
import logging

router = APIRouter()
attachment_service = AttachmentService()
logger = logging.getLogger(__name__)

@router.get("/", response_model=None, summary="获取任务列表（分页）")
async def get_tasks(
    page: Optional[int] = Query(None, ge=1, description="页码（如果提供，将覆盖skip）"),
    skip: Optional[int] = Query(None, ge=0, description="跳过的记录数（与page二选一）"),
    limit: Optional[int] = Query(None, ge=1, le=100, description="每页记录数"),
    size: Optional[int] = Query(None, ge=1, le=200, description="每页记录数（兼容前端参数）"),
    search: Optional[str] = Query(None, description="搜索关键词"),
    project_id: Optional[str] = Query(None, description="项目ID筛选"),
    assignee_id: Optional[str] = Query(None, description="负责人筛选"),
    reporter_id: Optional[str] = Query(None, description="报告人筛选"),
    status: Optional[str] = Query(None, description="状态筛选"),
    priority: Optional[str] = Query(None, description="优先级筛选"),
    task_type: Optional[str] = Query(None, description="任务类型筛选"),
    due_date_start: Optional[str] = Query(None, description="截止日期开始"),
    due_date_end: Optional[str] = Query(None, description="截止日期结束"),
    due_date_from: Optional[str] = Query(None, description="截止日期开始（兼容前端参数）"),
    due_date_to: Optional[str] = Query(None, description="截止日期结束（兼容前端参数）"),
    tags: Optional[str] = Query(None, description="标签筛选（逗号分隔）"),
    version: Optional[str] = Query(None, description="版本筛选"),
    version_id: Optional[str] = Query(None, description="版本ID筛选（兼容前端参数）"),
    requirement_id: Optional[str] = Query(None, description="需求ID筛选"),
    sort_by: str = Query("created_at", description="排序字段"),
    sort_order: Optional[str] = Query("desc", description="排序方向"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取任务列表
    """
    try:
        task_service = TaskService(db)
        result = await task_service.list_tasks(
            current_user=current_user,
            page=page,
            skip=skip,
            limit=limit,
            size=size,
            search=search,
            project_id=project_id,
            assignee_id=assignee_id,
            reporter_id=reporter_id,
            status=status,
            priority=priority,
            task_type=task_type,
            due_date_start=due_date_start,
            due_date_end=due_date_end,
            due_date_from=due_date_from,
            due_date_to=due_date_to,
            tags=tags,
            version=version,
            version_id=version_id,
            requirement_id=requirement_id,
            sort_by=sort_by,
            sort_order=sort_order
        )
        return paginated_response(
            items=result["items"],
            total=result["total"],
            page=result["page"],
            size=result["size"],
            message="获取任务列表成功"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务列表失败: {str(e)}", exc_info=True)
        return error_response(
            error_type=ErrorTypes.INTERNAL_SERVER_ERROR,
            message=f"获取任务列表失败: {str(e)}",
            code=500
        )

@router.get("/count", response_model=ApiResponse, summary="获取任务总数")
async def get_tasks_count(
    search: Optional[str] = Query(None, description="搜索关键词"),
    project_id: Optional[str] = Query(None, description="项目ID筛选"),
    assignee_id: Optional[str] = Query(None, description="负责人筛选"),
    reporter_id: Optional[str] = Query(None, description="报告人筛选"),
    status: Optional[str] = Query(None, description="状态筛选"),
    priority: Optional[str] = Query(None, description="优先级筛选"),
    task_type: Optional[str] = Query(None, description="任务类型筛选"),
    due_date_start: Optional[str] = Query(None, description="截止日期开始"),
    due_date_end: Optional[str] = Query(None, description="截止日期结束"),
    due_date_from: Optional[str] = Query(None, description="截止日期开始（兼容前端参数）"),
    due_date_to: Optional[str] = Query(None, description="截止日期结束（兼容前端参数）"),
    tags: Optional[str] = Query(None, description="标签筛选（逗号分隔）"),
    version: Optional[str] = Query(None, description="版本筛选"),
    version_id: Optional[str] = Query(None, description="版本ID筛选（兼容前端参数）"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取任务总数
    """
    # 构建查询条件（与get_tasks相同的逻辑）
    query = {}
    
    # 权限控制
    user_permissions = current_user.get("permissions", [])
    user_id = ObjectId(current_user.get("user_id"))
    
    if Permissions.TASK_READ_ALL not in user_permissions and Permissions.ADMIN not in user_permissions:
        user_projects = await db.projects.find({
            "$or": [
                {"is_public": True},
                {"created_by": user_id},
                {"members.user_id": user_id}
            ]
        }, {"_id": 1}).to_list(length=None)
        
        project_ids = [project["_id"] for project in user_projects]
        
        query["$or"] = [
            {"assignee_id": user_id},
            {"reporter_id": user_id},
            {"project_id": {"$in": project_ids}}
        ]
    
    # 处理日期参数兼容
    date_start = due_date_start or due_date_from
    date_end = due_date_end or due_date_to
    
    # 处理版本ID筛选 - 通过关联表查询（用于count接口）
    version_task_ids = None
    if version_id:
        try:
            version_obj_id = ObjectId(version_id)
            # 通过版本任务关联表查询关联的任务ID
            task_relations = await db.version_task_relations.find(
                {"version_id": version_obj_id}
            ).to_list(length=None)
            version_task_ids = [relation["task_id"] for relation in task_relations]
            if not version_task_ids:
                # 如果没有关联的任务，返回0
                return success_response(data={"total": 0})
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid version_id format"
            )
    
    # 兼容版本参数（如果提供了version但没有version_id）
    if version and not version_id:
        # 兼容旧版本参数，通过version字段筛选
        pass
    
    # 应用其他筛选条件（与get_tasks相同）
    if search:
        query["$and"] = query.get("$and", [])
        query["$and"].append({
            "$or": [
                {"title": {"$regex": search, "$options": "i"}},
                {"description": {"$regex": search, "$options": "i"}},
                {"tags": {"$in": [{"$regex": search, "$options": "i"}]}}
            ]
        })
    
    if project_id:
        try:
            query["project_id"] = ObjectId(project_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid project_id format"
            )
    
    if assignee_id:
        try:
            query["assignee_id"] = ObjectId(assignee_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid assignee_id format"
            )
    
    if reporter_id:
        try:
            query["reporter_id"] = ObjectId(reporter_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid reporter_id format"
            )
    
    if status:
        query["status"] = status
    
    if priority:
        query["priority"] = priority
    
    if task_type:
        query["type"] = task_type
    
    # 日期范围筛选 - 使用兼容后的参数
    if date_start or date_end:
        date_query = {}
        if date_start:
            try:
                date_query["$gte"] = datetime.fromisoformat(date_start.replace('Z', '+00:00'))
            except ValueError:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail="Invalid due_date_start format"
                )
        if date_end:
            try:
                date_query["$lte"] = datetime.fromisoformat(date_end.replace('Z', '+00:00'))
            except ValueError:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail="Invalid due_date_end format"
                )
        query["due_date"] = date_query
    
    if tags:
        tag_list = [tag.strip() for tag in tags.split(",")]
        query["tags"] = {"$in": tag_list}
    
    # 如果通过version_id筛选，添加任务ID限制
    if version_task_ids:
        query["_id"] = {"$in": version_task_ids}
    
    # 版本筛选 - 任务模块通过任务版本或项目版本筛选（仅当使用version参数且没有version_id时）
    if version and not version_id:
        query["$or"] = query.get("$or", [])
        if not isinstance(query["$or"], list):
            query["$or"] = []
        query["$or"].append({"version": version})
    
    # 统计总数
    total = await db.tasks.count_documents(query)
    
    return success_response(data={"total": total})

@router.get("/export", summary="导出任务为Excel")
async def export_tasks(
    project_id: Optional[str] = Query(None, description="项目ID筛选"),
    status: Optional[str] = Query(None, description="状态筛选"),
    priority: Optional[str] = Query(None, description="优先级筛选"),
    assignee_id: Optional[str] = Query(None, description="负责人筛选"),
    search: Optional[str] = Query(None, description="搜索关键词"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    导出任务数据为Excel文件
    """
    # 检查权限
    user_permissions = current_user.get("permissions", [])
    if Permissions.TASK_READ not in user_permissions and Permissions.ADMIN not in user_permissions:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions to export tasks"
        )
    
    # 处理空字符串参数（前端可能传递空字符串）
    if project_id == "":
        project_id = None
    if assignee_id == "":
        assignee_id = None
    if status == "":
        status = None
    if priority == "":
        priority = None
    if search == "":
        search = None
    
    # 构建查询条件（复用 get_tasks 的逻辑）
    user_id = ObjectId(current_user.get("user_id"))
    query = {}
    
    # 权限过滤：非管理员只能看到自己有权限的任务
    if Permissions.TASK_READ_ALL not in user_permissions and Permissions.ADMIN not in user_permissions:
        # 获取用户有权限的项目列表
        project_ids = []
        projects_cursor = db.projects.find({
            "$or": [
                {"is_public": True},
                {"created_by": user_id},
                {"members.user_id": user_id}
            ]
        }, {"_id": 1})
        try:
            async for project in projects_cursor:
                project_ids.append(project["_id"])
        finally:
            # 确保游标被关闭
            if hasattr(projects_cursor, 'close'):
                await projects_cursor.close()
        
        query["$or"] = [
            {"assignee_id": user_id},
            {"reporter_id": user_id},
            {"project_id": {"$in": project_ids}}
        ]
    
    # 应用筛选条件
    if project_id:
        try:
            query["project_id"] = ObjectId(project_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid project_id format"
            )
    
    if assignee_id:
        try:
            query["assignee_id"] = ObjectId(assignee_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid assignee_id format"
            )
    
    if status:
        # 验证状态值（使用动态状态验证）
        validator = get_state_validator(db)
        is_valid = await validator.validate_state_code("task", status)
        if not is_valid:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid status value: {status}"
            )
        query["status"] = status
    
    if priority:
        # 验证优先级值（从优先级管理表验证）
        priority_doc = await db.priorities.find_one({
            "module_type": "task",
            "code": priority,
            "is_enabled": True
        })
        if not priority_doc:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid priority value: {priority}"
            )
        query["priority"] = priority
    
    if search:
        # 如果已经有 $or 条件（权限过滤），需要合并
        if "$or" in query:
            existing_or = query["$or"]
            if isinstance(existing_or, list):
                # 创建新的 $and 条件来合并权限过滤和搜索
                query = {
                    "$and": [
                        {"$or": existing_or},
                        {
                            "$or": [
                                {"title": {"$regex": search, "$options": "i"}},
                                {"description": {"$regex": search, "$options": "i"}}
                            ]
                        }
                    ]
                }
            else:
                query["$or"] = [
                    existing_or,
                    {"title": {"$regex": search, "$options": "i"}},
                    {"description": {"$regex": search, "$options": "i"}}
                ]
        else:
            query["$or"] = [
                {"title": {"$regex": search, "$options": "i"}},
                {"description": {"$regex": search, "$options": "i"}}
            ]
    
    # 查询任务数据（不限制数量）
    cursor = db.tasks.find(query).sort("created_at", DESCENDING)
    tasks = await cursor.to_list(length=None)
    
    # 收集用户ID和项目ID
    user_ids = set()
    project_ids = set()
    for task in tasks:
        if task.get("assignee_id"):
            user_ids.add(task["assignee_id"])
        if task.get("reporter_id"):
            user_ids.add(task["reporter_id"])
        if task.get("created_by"):
            user_ids.add(task["created_by"])
        if task.get("project_id"):
            project_ids.add(task["project_id"])
    
    # 批量查询用户信息
    users_dict = {}
    if user_ids:
        users_cursor = db.users.find({"_id": {"$in": list(user_ids)}}, {"_id": 1, "name": 1})
        try:
            async for user in users_cursor:
                users_dict[str(user["_id"])] = user.get("name", "")
        finally:
            # 确保游标被关闭
            if hasattr(users_cursor, 'close'):
                await users_cursor.close()
    
    # 批量查询项目信息
    projects_dict = {}
    if project_ids:
        projects_cursor = db.projects.find({"_id": {"$in": list(project_ids)}}, {"_id": 1, "name": 1})
        try:
            async for project in projects_cursor:
                projects_dict[str(project["_id"])] = project.get("name", "")
        finally:
            # 确保游标被关闭
            if hasattr(projects_cursor, 'close'):
                await projects_cursor.close()
    
    # 创建Excel工作簿
    wb = Workbook()
    ws = wb.active
    ws.title = "任务列表"
    
    # 设置表头
    headers = [
        "任务ID", "标题", "描述", "项目", "任务类型", "状态", "优先级",
        "负责人", "报告人", "创建人", "进度(%)", "预估工时", "实际工时",
        "开始日期", "截止日期", "标签", "创建时间", "更新时间"
    ]
    ws.append(headers)
    
    # 设置表头样式
    header_font = Font(bold=True, color="FFFFFF")
    header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
    header_alignment = Alignment(horizontal="center", vertical="center")
    
    for col_num, header in enumerate(headers, 1):
        cell = ws.cell(row=1, column=col_num)
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = header_alignment
    
    # 填充数据
    for task in tasks:
        assignee_name = users_dict.get(str(task.get("assignee_id", "")), "") if task.get("assignee_id") else ""
        reporter_name = users_dict.get(str(task.get("reporter_id", "")), "") if task.get("reporter_id") else ""
        creator_name = users_dict.get(str(task.get("created_by", "")), "") if task.get("created_by") else ""
        project_name = projects_dict.get(str(task.get("project_id", "")), "") if task.get("project_id") else ""
        
        row_data = [
            str(task.get("_id", "")),
            task.get("title", ""),
            task.get("description", ""),
            project_name,
            task.get("type", ""),
            task.get("status", ""),
            task.get("priority", ""),
            assignee_name,
            reporter_name,
            creator_name,
            task.get("progress", 0),
            task.get("estimated_hours", 0),
            task.get("actual_hours", 0),
            task.get("start_date", "").strftime("%Y-%m-%d") if task.get("start_date") else "",
            task.get("due_date", "").strftime("%Y-%m-%d") if task.get("due_date") else "",
            ", ".join(task.get("tags", [])),
            task.get("created_at", "").strftime("%Y-%m-%d %H:%M:%S") if task.get("created_at") else "",
            task.get("updated_at", "").strftime("%Y-%m-%d %H:%M:%S") if task.get("updated_at") else ""
        ]
        ws.append(row_data)
    
    # 调整列宽
    column_widths = [25, 30, 40, 20, 12, 12, 12, 15, 15, 15, 10, 12, 12, 12, 12, 30, 20, 20]
    for col_num, width in enumerate(column_widths, 1):
        ws.column_dimensions[ws.cell(row=1, column=col_num).column_letter].width = width
    
    # 保存到内存
    output = io.BytesIO()
    wb.save(output)
    output.seek(0)
    
    # 读取文件内容
    file_content = output.read()
    
    # 生成文件名
    filename = f"tasks_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
    
    # 编码文件名（文件名本身是 ASCII，直接使用）
    content_disposition = f"attachment; filename=\"{filename}\""
    
    # 返回文件流
    return StreamingResponse(
        io.BytesIO(file_content),
        media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        headers={"Content-Disposition": content_disposition}
    )

@router.get("/import/template", summary="下载任务导入模板")
async def download_import_template(
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    下载任务导入模板文件
    """
    # 检查权限
    user_permissions = current_user.get("permissions", [])
    if Permissions.TASK_CREATE not in user_permissions and Permissions.ADMIN not in user_permissions:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions to download import template"
        )
    
    try:
        # 创建Excel工作簿
        wb = Workbook()
        ws = wb.active
        ws.title = "任务导入模板"
        
        # 设置表头
        headers = [
            "标题", "描述", "项目", "任务类型", "状态", "优先级",
            "负责人", "报告人", "进度(%)", "预估工时", "实际工时",
            "开始日期", "截止日期", "标签"
        ]
        ws.append(headers)
        
        # 设置表头样式
        header_font = Font(bold=True, color="FFFFFF", size=11)
        header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
        header_alignment = Alignment(horizontal="center", vertical="center")
        
        for col_num, header in enumerate(headers, 1):
            cell = ws.cell(row=1, column=col_num)
            cell.font = header_font
            cell.fill = header_fill
            cell.alignment = header_alignment
        
        # 添加说明行（第二行）
        instructions = [
            "必填", "可选", "必填（项目名称）", "可选（feature/bug/task）", "可选（todo/in_progress/review/done）", "可选（low/medium/high/urgent）",
            "可选（用户姓名）", "可选（用户姓名）", "可选（0-100）", "可选（小时数）", "可选（小时数）",
            "可选（YYYY-MM-DD）", "可选（YYYY-MM-DD）", "可选（逗号分隔）"
        ]
        ws.append(instructions)
        
        # 设置说明行样式
        instruction_font = Font(italic=True, color="666666", size=9)
        instruction_alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
        for col_num, instruction in enumerate(instructions, 1):
            cell = ws.cell(row=2, column=col_num)
            cell.font = instruction_font
            cell.alignment = instruction_alignment
        
        # 添加示例数据行（第三行）
        example_data = [
            "示例任务标题", "这是任务描述", "示例项目", "feature", "", "medium",
            "张三", "李四", "0", "8", "0", "2024-01-01", "2024-01-31", "重要,紧急"
        ]
        ws.append(example_data)
        
        # 设置示例数据样式
        example_font = Font(color="999999", size=10)
        for col_num in range(1, len(headers) + 1):
            cell = ws.cell(row=3, column=col_num)
            cell.font = example_font
        
        # 调整列宽
        column_widths = [25, 40, 20, 15, 15, 12, 15, 15, 12, 12, 12, 15, 15, 30]
        for col_num, width in enumerate(column_widths, 1):
            ws.column_dimensions[ws.cell(row=1, column=col_num).column_letter].width = width
        
        # 添加数据验证说明（在第四行添加）
        ws.append([])  # 空行
        ws.append(["注意事项："])
        ws.append(["1. 必填字段：标题、项目（项目名称必须已存在）"])
        ws.append(["2. 项目名称必须与系统中已存在的项目名称完全一致"])
        ws.append(["3. 负责人和报告人使用用户姓名，必须与系统中已存在的用户姓名完全一致"])
        ws.append(["4. 日期格式：YYYY-MM-DD，例如：2024-01-01"])
        ws.append(["5. 标签多个值用逗号分隔，例如：重要,紧急,bug"])
        ws.append(["6. 状态和优先级如果不填写，将使用默认值（todo, medium）"])
        ws.append(["7. 进度范围：0-100"])
        
        # 设置注意事项样式
        note_font = Font(size=10, color="000000")
        note_alignment = Alignment(horizontal="left", vertical="top", wrap_text=True)
        for row_idx in range(4, 12):
            for col_num in range(1, 2):
                cell = ws.cell(row=row_idx, column=col_num)
                cell.font = note_font
                cell.alignment = note_alignment
        
        # 冻结前两行（表头和说明）
        ws.freeze_panes = "A3"
        
        # 保存到内存
        output = io.BytesIO()
        wb.save(output)
        output.seek(0)
        
        # 生成文件名
        filename_cn = f"任务导入模板_{datetime.now().strftime('%Y%m%d')}.xlsx"
        filename_en = f"task_import_template_{datetime.now().strftime('%Y%m%d')}.xlsx"
        
        # 编码文件名以支持中文（使用 RFC 5987 格式）
        # 对中文文件名进行 UTF-8 URL 编码（quote 返回的是纯 ASCII 字符串）
        encoded_filename = quote(filename_cn.encode('utf-8'), safe='')
        # 使用 filename 提供 ASCII fallback，filename* 提供 UTF-8 编码的中文名
        # content_disposition 字符串现在完全由 ASCII 字符组成，可以安全编码为 latin-1
        content_disposition = f"attachment; filename=\"{filename_en}\"; filename*=UTF-8''{encoded_filename}"
        
        # 返回文件流
        return StreamingResponse(
            io.BytesIO(output.read()),
            media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            headers={"Content-Disposition": content_disposition}
        )
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f"生成导入模板失败: {str(e)}", exc_info=True)
        return error_response(
            error_type=ErrorTypes.IMPORT_TEMPLATE_ERROR,
            message=f"生成导入模板失败: {str(e)}",
            code=500
        )

@router.get("/{task_id}", response_model=ApiResponse, summary="获取任务详情")
async def get_task(
    task_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取任务详情
    """
    try:
        task_service = TaskService(db)
        task_data = await task_service.get_task(task_id, current_user)
        return success_response(data=task_data, message="获取任务详情成功")
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取任务详情失败: {str(e)}", exc_info=True)
        return error_response(
            error_type=ErrorTypes.INTERNAL_SERVER_ERROR,
            message=f"获取任务详情失败: {str(e)}",
            code=500
        )

@router.post("/", include_in_schema=False, response_model=ApiResponse, summary="创建任务")
@router.post("", include_in_schema=True, response_model=ApiResponse, summary="创建任务")
async def create_task_post(
    task_in: TaskCreate,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    创建任务（兼容前端 POST /tasks/ 请求）
    """
    try:
        task_service = TaskService(db)
        task_data = await task_service.create_task(task_in, current_user)
        return success_response(data=task_data, message="创建任务成功")
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"创建任务失败: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"创建任务失败: {str(e)}"
        )
    # 检查权限
    user_permissions = current_user.get("permissions", [])
    if Permissions.TASK_CREATE not in user_permissions and Permissions.ADMIN not in user_permissions:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions to create task"
        )
    
    # 验证项目是否存在且有权限
    if task_in.project_id:
        try:
            project_object_id = ObjectId(task_in.project_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid project_id format"
            )
        
        project = await db.projects.find_one({"_id": project_object_id})
        if not project:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Project not found"
            )
        
        # 检查项目访问权限
        user_id = ObjectId(current_user.get("user_id"))
        has_project_access = (
            Permissions.PROJECT_READ_ALL in user_permissions or
            Permissions.ADMIN in user_permissions or
            project["is_public"] or
            project["created_by"] == user_id or
            any(member["user_id"] == user_id for member in project.get("members", []))
        )
        
        if not has_project_access:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to create task in this project"
            )
    
    # 验证负责人是否存在
    if task_in.assignee_id:
        try:
            assignee_object_id = ObjectId(task_in.assignee_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid assignee_id format"
            )
        
        assignee = await db.users.find_one({"_id": assignee_object_id})
        if not assignee:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Assignee not found"
            )
    
    # 创建任务文档
    task_doc = task_in.model_dump()
    # 修复字段名，将task_type映射到数据库的type字段
    if "task_type" in task_doc:
        task_doc["type"] = task_doc.pop("task_type")
    task_doc.update({
        "created_by": ObjectId(current_user.get("user_id")),
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
        "comments": [],
        "attachments": [],
        "time_logs": []
    })
    
    # 转换ObjectId字段
    if task_doc.get("project_id"):
        task_doc["project_id"] = ObjectId(task_doc["project_id"])
    if task_doc.get("assignee_id"):
        task_doc["assignee_id"] = ObjectId(task_doc["assignee_id"])
    if task_doc.get("reporter_id"):
        task_doc["reporter_id"] = ObjectId(task_doc["reporter_id"])
    if task_doc.get("requirement_id"):
        task_doc["requirement_id"] = ObjectId(task_doc["requirement_id"])
    if task_doc.get("dependencies"):
        task_doc["dependencies"] = [ObjectId(dep) for dep in task_doc["dependencies"]]
    
    # 插入数据库
    result = await db.tasks.insert_one(task_doc)
    
    # 获取创建的任务
    created_task = await db.tasks.find_one({"_id": result.inserted_id})
    if not created_task:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to create task"
        )
    
    # 转换为响应模型
    task_data = {
        "id": str(created_task["_id"]),
        "title": created_task["title"],
        "description": created_task.get("description"),
        "task_type": created_task["type"],  # 从数据库的type字段映射到前端的task_type
        "status": created_task["status"],
        "priority": created_task["priority"],
        "project_id": str(created_task["project_id"]) if created_task.get("project_id") else None,
        "assignee_id": str(created_task["assignee_id"]) if created_task.get("assignee_id") else None,
        "reporter_id": str(created_task["reporter_id"]) if created_task.get("reporter_id") else None,
        "estimated_hours": created_task.get("estimated_hours"),
        "actual_hours": created_task.get("actual_hours"),
        "start_date": created_task.get("start_date"),
        "due_date": created_task.get("due_date"),
        "tags": created_task.get("tags", []),
        "dependencies": [str(dep) for dep in created_task.get("dependencies", [])],
        "organization_id": str(created_task["organization_id"]) if created_task.get("organization_id") else None,
        "comments": [],
        "attachments": [],
        "time_logs": [],
        "created_by": str(created_task["created_by"]),
        "created_at": created_task["created_at"],
        "updated_at": created_task["updated_at"],
        "comment_count": 0,
        "attachment_count": 0,
        "total_time_spent": 0.0
    }
    
    # 触发通知：如果任务有负责人，通知负责人
    if created_task.get("assignee_id") and str(created_task["assignee_id"]) != current_user.get("user_id"):
        try:
            assignee = await db.users.find_one({"_id": created_task["assignee_id"]})
            creator = await db.users.find_one({"_id": ObjectId(current_user.get("user_id"))})
            creator_name = creator.get("name") or creator.get("username", "系统") if creator else "系统"
            
            notification_data = NotificationCreate(
                user_id=str(created_task["assignee_id"]),
                type=NotificationType.TASK_ASSIGNED,
                title=f"新任务分配：{created_task['title']}",
                content=f"{creator_name} 将任务「{created_task['title']}」分配给了您",
                related_type=RelatedObjectType.TASK,
                related_id=str(created_task["_id"])
            )
            # 异步发送通知，不阻塞响应
            asyncio.create_task(
                NotificationService.create_notification(db=db, notification_data=notification_data, send_email=True)
            )
        except Exception as e:
            logger.warning(f"发送任务分配通知失败: {str(e)}")
    
    return success_response(data=task_data, message="创建任务成功")

@router.post("/create_task", response_model=ApiResponse, summary="创建任务")
async def create_task(
    task_in: TaskCreate,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    创建任务
    """
    try:
        task_service = TaskService(db)
        task_data = await task_service.create_task(task_in, current_user)
        return success_response(data=task_data, message="创建任务成功")
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"创建任务失败: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"创建任务失败: {str(e)}"
        )
    # 检查权限
    user_permissions = current_user.get("permissions", [])
    if Permissions.TASK_CREATE not in user_permissions and Permissions.ADMIN not in user_permissions:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions to create task"
        )
    
    # 验证项目是否存在且有权限
    if task_in.project_id:
        try:
            project_object_id = ObjectId(task_in.project_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid project_id format"
            )
        
        project = await db.projects.find_one({"_id": project_object_id})
        if not project:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Project not found"
            )
        
        # 检查项目访问权限
        user_id = ObjectId(current_user.get("user_id"))
        has_project_access = (
            Permissions.PROJECT_READ_ALL in user_permissions or
            Permissions.ADMIN in user_permissions or
            project["is_public"] or
            project["created_by"] == user_id or
            any(member["user_id"] == user_id for member in project.get("members", []))
        )
        
        if not has_project_access:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to create task in this project"
            )
    
    # 验证负责人是否存在
    if task_in.assignee_id:
        try:
            assignee_object_id = ObjectId(task_in.assignee_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid assignee_id format"
            )
        
        assignee = await db.users.find_one({"_id": assignee_object_id})
        if not assignee:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Assignee not found"
            )
    
    # 创建任务文档
    task_doc = task_in.model_dump()
    # 修复字段名，将task_type映射到数据库的type字段
    if "task_type" in task_doc:
        task_doc["type"] = task_doc.pop("task_type")
    task_doc.update({
        "created_by": ObjectId(current_user.get("user_id")),
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
        "comments": [],
        "attachments": [],
        "time_logs": []
    })
    
    # 转换ObjectId字段
    if task_doc.get("project_id"):
        task_doc["project_id"] = ObjectId(task_doc["project_id"])
    if task_doc.get("assignee_id"):
        task_doc["assignee_id"] = ObjectId(task_doc["assignee_id"])
    if task_doc.get("reporter_id"):
        task_doc["reporter_id"] = ObjectId(task_doc["reporter_id"])
    if task_doc.get("requirement_id"):
        task_doc["requirement_id"] = ObjectId(task_doc["requirement_id"])
    if task_doc.get("dependencies"):
        task_doc["dependencies"] = [ObjectId(dep) for dep in task_doc["dependencies"]]
    
    # 插入数据库
    result = await db.tasks.insert_one(task_doc)
    
    # 获取创建的任务
    created_task = await db.tasks.find_one({"_id": result.inserted_id})
    if not created_task:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to create task"
        )
    
    # 转换为响应模型
    task_data = {
        "id": str(created_task["_id"]),
        "title": created_task["title"],
        "description": created_task.get("description"),
        "task_type": created_task["type"],  # 从数据库的type字段映射到前端的task_type
        "status": created_task["status"],
        "priority": created_task["priority"],
        "project_id": str(created_task["project_id"]) if created_task.get("project_id") else None,
        "assignee_id": str(created_task["assignee_id"]) if created_task.get("assignee_id") else None,
        "reporter_id": str(created_task["reporter_id"]) if created_task.get("reporter_id") else None,
        "estimated_hours": created_task.get("estimated_hours"),
        "actual_hours": created_task.get("actual_hours"),
        "start_date": created_task.get("start_date"),
        "due_date": created_task.get("due_date"),
        "tags": created_task.get("tags", []),
        "dependencies": [str(dep) for dep in created_task.get("dependencies", [])],
        "organization_id": str(created_task["organization_id"]) if created_task.get("organization_id") else None,
        "comments": [],
        "attachments": [],
        "time_logs": [],
        "created_by": str(created_task["created_by"]),
        "created_at": created_task["created_at"],
        "updated_at": created_task["updated_at"],
        "comment_count": 0,
        "attachment_count": 0,
        "total_time_spent": 0.0
    }
    
    # 触发通知：如果任务有负责人，通知负责人
    if created_task.get("assignee_id") and str(created_task["assignee_id"]) != current_user.get("user_id"):
        try:
            assignee = await db.users.find_one({"_id": created_task["assignee_id"]})
            creator = await db.users.find_one({"_id": ObjectId(current_user.get("user_id"))})
            creator_name = creator.get("name") or creator.get("username", "系统") if creator else "系统"
            
            notification_data = NotificationCreate(
                user_id=str(created_task["assignee_id"]),
                type=NotificationType.TASK_ASSIGNED,
                title=f"新任务分配：{created_task['title']}",
                content=f"{creator_name} 将任务「{created_task['title']}」分配给了您",
                related_type=RelatedObjectType.TASK,
                related_id=str(created_task["_id"])
            )
            # 异步发送通知，不阻塞响应
            asyncio.create_task(
                NotificationService.create_notification(db=db, notification_data=notification_data, send_email=True)
            )
        except Exception as e:
            logger.warning(f"发送任务分配通知失败: {str(e)}")
    
    return success_response(data=task_data, message="创建任务成功")

@router.put("/{task_id}", response_model=ApiResponse, summary="更新任务")
async def update_task(
    task_id: str,
    task_in: TaskUpdate,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    更新任务信息
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid task ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 权限检查
    project_id = task.get("project_id")
    if not project_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Task has no project_id"
        )
    
    project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
    
    # 检查项目成员权限
    from app.services.permission_service import PermissionService
    try:
        await PermissionService.require_project_member_permission(
            project_id_str,
            current_user,
            db,
            Permissions.TASK_UPDATE
        )
    except PermissionError as e:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=str(e)
        )
    
    # 检查是否允许更新（保留原有逻辑，允许负责人、报告人、创建者更新）
    user_id = ObjectId(current_user.get("user_id"))
    is_allowed = (
        task.get("assignee_id") == user_id or
        task.get("reporter_id") == user_id or
        task.get("created_by") == user_id
    )
    
    if not is_allowed:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions to update this task"
        )
    
    # 构建更新数据
    update_data = task_in.model_dump(exclude_unset=True)
    
    # 转换ObjectId字段
    if "project_id" in update_data and update_data["project_id"]:
        try:
            update_data["project_id"] = ObjectId(update_data["project_id"])
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid project_id format"
            )
    
    if "assignee_id" in update_data and update_data["assignee_id"]:
        try:
            update_data["assignee_id"] = ObjectId(update_data["assignee_id"])
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid assignee_id format"
            )
    
    if "reporter_id" in update_data and update_data["reporter_id"]:
        try:
            update_data["reporter_id"] = ObjectId(update_data["reporter_id"])
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid reporter_id format"
            )
    
    if "dependencies" in update_data and update_data["dependencies"]:
        try:
            update_data["dependencies"] = [ObjectId(dep) for dep in update_data["dependencies"]]
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid dependencies format"
            )
    
    # 处理空字符串和None值
    # 对于可选字段，如果值为空字符串，应该设置为None或删除该字段
    fields_to_unset = []
    for key, value in list(update_data.items()):
        if value == "" or value is None:
            # 对于可选字段，如果为空字符串，从更新数据中移除（保持原值）
            # 或者设置为None（如果明确需要清空）
            if key in ["assignee_id", "reporter_id", "parent_task_id", "description"]:
                if value == "":
                    update_data[key] = None
                elif value is None:
                    # 如果明确设置为None，保留在更新数据中
                    pass
            elif key in ["start_date", "due_date"]:
                if value == "":
                    update_data[key] = None
            elif key in ["tags"]:
                if value == []:
                    update_data[key] = []
            elif key in ["progress", "estimated_hours", "actual_hours"]:
                # 数值字段：如果为0或有效数字，保留；如果为None或空字符串，设置为0
                if value == "" or value is None:
                    update_data[key] = 0
                elif isinstance(value, (int, float)):
                    # 确保进度在0-100范围内
                    if key == "progress":
                        update_data[key] = max(0, min(100, int(value)))
                    else:
                        update_data[key] = max(0, float(value))
            else:
                # 其他字段，如果为空字符串，移除该字段
                if value == "":
                    fields_to_unset.append(key)
                    del update_data[key]
    
    # 检查状态是否更新为已完成，如果是则自动设置进度为100
    # 完全依赖状态管理中的数据，通过状态名称或描述判断是否为已完成状态
    if "status" in update_data:
        new_status = update_data["status"]
        validator = get_state_validator(db)
        try:
            # 获取状态信息
            state_info = await validator.get_state_info("task", new_status)
            if state_info:
                # 获取状态名称和描述
                state_name = ""
                state_description = ""
                if hasattr(state_info, "name"):
                    state_name = getattr(state_info, "name", "")
                    state_description = getattr(state_info, "description", "") or ""
                elif isinstance(state_info, dict):
                    state_name = state_info.get("name", "")
                    state_description = state_info.get("description", "") or ""
                
                # 通过状态名称或描述判断是否为已完成状态（完全依赖状态管理中的数据）
                # 检查状态名称或描述中是否包含"已完成"、"完成"等关键词
                completed_keywords = ["已完成", "完成", "完成状态"]
                is_completed = any(keyword in state_name or keyword in state_description 
                                 for keyword in completed_keywords)
                
                if is_completed:
                    # 如果状态更新为已完成，自动设置进度为100
                    update_data["progress"] = 100
        except Exception:
            # 如果获取状态信息失败，不进行任何操作，避免错误判断
            pass
    
    # 更新任务
    if update_data:
        update_data["updated_at"] = datetime.utcnow()
        update_op = {"$set": update_data}
        
        # 如果有需要删除的字段
        if fields_to_unset:
            update_op["$unset"] = {field: "" for field in fields_to_unset}
        
        await db.tasks.update_one(
            {"_id": object_id},
            update_op
        )
    
    # 获取更新后的任务
    updated_task = await db.tasks.find_one({"_id": object_id})
    
    # 获取关联信息
    assignee_info = None
    reporter_info = None
    project_info = None
    creator_info = None
    
    if updated_task.get("assignee_id"):
        assignee_info = await db.users.find_one(
            {"_id": updated_task["assignee_id"]},
            {"_id": 1, "name": 1, "avatar": 1}
        )
    
    if updated_task.get("reporter_id"):
        reporter_info = await db.users.find_one(
            {"_id": updated_task["reporter_id"]},
            {"_id": 1, "name": 1, "avatar": 1}
        )
    
    if updated_task.get("created_by"):
        creator_info = await db.users.find_one(
            {"_id": updated_task["created_by"]},
            {"_id": 1, "name": 1, "avatar": 1}
        )
    
    if updated_task.get("project_id"):
        project_info = await db.projects.find_one(
            {"_id": updated_task["project_id"]},
            {"_id": 1, "name": 1}
        )
    
    # 获取任务关联的版本（取第一个）
    version_info = None
    task_relation = await db.version_task_relations.find_one(
        {"task_id": updated_task["_id"]}
    )
    if task_relation:
        version = await db.versions.find_one(
            {"_id": task_relation["version_id"]},
            {"_id": 1, "name": 1, "version_number": 1}
        )
        if version:
            version_info = version
    
    # 使用工具函数格式化响应
    task_data = format_task_response(
        task=updated_task,
        assignee_info=assignee_info,
        reporter_info=reporter_info,
        creator_info=creator_info,
        project_info=project_info,
        version_info=version_info,
        include_details=True
    )
    
    # 触发通知：任务更新时通知负责人
    if updated_task.get("assignee_id") and str(updated_task["assignee_id"]) != current_user.get("user_id"):
        try:
            creator = await db.users.find_one({"_id": ObjectId(current_user.get("user_id"))})
            creator_name = creator.get("name") or creator.get("username", "系统") if creator else "系统"
            task_title = updated_task.get("title", "任务")
            
            notification_data = NotificationCreate(
                user_id=str(updated_task["assignee_id"]),
                type=NotificationType.TASK_UPDATED,
                title=f"任务更新：{task_title}",
                content=f"{creator_name} 更新了任务「{task_title}」",
                related_type=RelatedObjectType.TASK,
                related_id=task_id
            )
            # 异步发送通知，不阻塞响应
            asyncio.create_task(
                NotificationService.create_notification(db=db, notification_data=notification_data, send_email=False)
            )
        except Exception as e:
            logger.warning(f"发送任务更新通知失败: {str(e)}")
    
    return success_response(data=task_data, message="更新任务成功")

@router.delete("/{task_id}", response_model=ApiResponse, summary="删除任务")
async def delete_task(
    task_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    删除任务
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid task ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 权限检查
    project_id = task.get("project_id")
    if not project_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Task has no project_id"
        )
    
    project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
    
    # 检查项目成员权限
    from app.services.permission_service import PermissionService
    try:
        await PermissionService.require_project_member_permission(
            project_id_str,
            current_user,
            db,
            Permissions.TASK_DELETE
        )
    except PermissionError as e:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=str(e)
        )
    
    # 检查是否允许删除（保留原有逻辑，允许创建者删除）
    user_id = ObjectId(current_user.get("user_id"))
    is_creator = task.get("created_by") == user_id
    if not is_creator:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions to delete this task"
        )
    
    # 触发通知：任务删除时通知负责人
    if task.get("assignee_id") and str(task["assignee_id"]) != current_user.get("user_id"):
        try:
            creator = await db.users.find_one({"_id": ObjectId(current_user.get("user_id"))})
            creator_name = creator.get("name") or creator.get("username", "系统") if creator else "系统"
            task_title = task.get("title", "任务")
            
            notification_data = NotificationCreate(
                user_id=str(task["assignee_id"]),
                type=NotificationType.TASK_DELETED,
                title=f"任务已删除：{task_title}",
                content=f"{creator_name} 删除了任务「{task_title}」",
                related_type=RelatedObjectType.TASK,
                related_id=task_id
            )
            # 异步发送通知，不阻塞响应
            asyncio.create_task(
                NotificationService.create_notification(db=db, notification_data=notification_data, send_email=False)
            )
        except Exception as e:
            logger.warning(f"发送任务删除通知失败: {str(e)}")
    
    # 删除任务
    await db.tasks.delete_one({"_id": object_id})
    
    return success_response(message="删除任务成功")

@router.patch("/{task_id}/status", response_model=ApiResponse, summary="更新任务状态")
async def update_task_status(
    task_id: str,
    status_update: TaskStatusUpdate,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    更新任务状态
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid task ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            await PermissionService.require_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:update"
            )
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=str(e) or "Not enough permissions to update task status"
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_permission = (
            Permissions.TASK_UPDATE in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("assignee_id") == user_id or
            task.get("reporter_id") == user_id or
            task.get("created_by") == user_id
        )
        
        if not has_permission:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to update task status"
            )
    
    # 验证状态代码是否有效
    # 直接从状态管理数据库表中获取数据验证，不使用缓存
    validator = get_state_validator(db)
    
    # 强制从数据库读取最新状态数据（不使用缓存）
    from app.core.state_cache import state_cache
    import logging
    logger = logging.getLogger(__name__)
    
    # 直接从数据库查询状态数据
    states_cursor = db.states.find({
        "module_type": "task",
        "is_enabled": True
    }).sort("sort_order", 1)
    states_list = await states_cursor.to_list(length=None)
    
    # 验证状态代码是否存在于数据库中
    valid_status_codes = [state.get("code") for state in states_list if state.get("code")]
    is_valid = status_update.status in valid_status_codes
    
    if not is_valid:
        logger.warning(f"状态验证失败: {status_update.status}, 数据库中的有效状态代码: {valid_status_codes}")
        
        # 兼容性处理：如果前端发送的是 review，但数据库中是 in_review，进行映射
        if status_update.status == "review":
            # 检查数据库中是否存在 in_review 状态
            in_review_state = next((state for state in states_list if state.get("code") == "in_review"), None)
            
            if not in_review_state:
                # 如果数据库中不存在 in_review 状态，尝试从数据库读取或创建一个默认的 in_review 状态
                existing_in_review = await db.states.find_one({
                    "module_type": "task",
                    "code": "in_review"
                })
                
                now = datetime.utcnow()
                if existing_in_review:
                    # 如果状态存在但被禁用，将其重新启用
                    await db.states.update_one(
                        {"_id": existing_in_review["_id"]},
                        {
                            "$set": {
                                "name": existing_in_review.get("name") or "审查中",
                                "color": existing_in_review.get("color") or "#722ed1",
                                "sort_order": existing_in_review.get("sort_order", 4),
                                "description": existing_in_review.get("description") or "任务处于审查阶段",
                                "is_enabled": True,
                                "is_visible": True,
                                "updated_at": now
                            }
                        }
                    )
                    in_review_state = await db.states.find_one({"_id": existing_in_review["_id"]})
                else:
                    # 数据库中完全没有该状态，创建一个默认状态
                    new_state = {
                        "name": "审查中",
                        "code": "in_review",
                        "module_type": "task",
                        "color": "#722ed1",
                        "sort_order": 4,
                        "description": "任务处于审查阶段",
                        "is_enabled": True,
                        "is_visible": True,
                        "is_default": False,
                        "created_at": now,
                        "updated_at": now
                    }
                    insert_result = await db.states.insert_one(new_state)
                    in_review_state = await db.states.find_one({"_id": insert_result.inserted_id})
                
                # 刷新状态缓存
                state_cache.invalidate_cache("task")
                
                if in_review_state:
                    states_list.append(in_review_state)
                    valid_status_codes.append("in_review")
            
            if in_review_state:
                # 使用 in_review 代替 review
                status_update.status = "in_review"
                is_valid = True
                logger.info("状态映射: review -> in_review（自动补充 in_review 状态）")
        
        if not is_valid:
            return error_response(
                error_type=ErrorTypes.INVALID_PARAMETER,
                message=f"Invalid status value: {status_update.status}. Valid statuses: {', '.join(valid_status_codes)}. Please ensure task states are initialized.",
                code=400
            )
    
    # 检查状态是否为已完成状态，如果是则自动设置进度为100
    # 使用动态状态管理检查是否为已完成状态，直接从数据库获取状态信息
    update_fields = {
        "status": status_update.status,
        "updated_at": datetime.utcnow()
    }
    
    # 从已查询的状态列表中查找当前状态信息
    # 使用刚才从数据库查询的 states_list，确保数据是最新的
    try:
        current_state = None
        for state in states_list:
            if state.get("code") == status_update.status:
                current_state = state
                break
        
        if current_state:
            # 获取状态名称和描述
            state_name = current_state.get("name", "")
            state_description = current_state.get("description", "") or ""
            
            # 通过状态名称或描述判断是否为已完成状态（完全依赖状态管理数据库中的数据）
            # 检查状态名称或描述中是否包含"已完成"、"完成"等关键词
            completed_keywords = ["已完成", "完成", "完成状态"]
            is_completed = any(keyword in state_name or keyword in state_description 
                             for keyword in completed_keywords)
            
            if is_completed:
                # 如果状态更新为已完成，自动设置进度为100
                update_fields["progress"] = 100
                logger.info(f"状态 {status_update.status} ({state_name}) 被识别为已完成状态，自动设置进度为100%")
                
                # 如果实际工时还未设置，且任务有开始时间，则根据开始时间和当前时间计算实际工时
                current_actual_hours = task.get("actual_hours")
                time_logs = task.get("time_logs", [])
                
                # 如果实际工时未设置或为0，且没有工时记录，则根据开始时间计算
                if (current_actual_hours is None or current_actual_hours == 0) and not time_logs:
                    start_date = task.get("start_date")
                    if start_date:
                        # 计算从开始时间到当前时间的小时数
                        current_time = datetime.utcnow()
                        if isinstance(start_date, datetime):
                            time_diff = current_time - start_date
                            # 转换为小时数（保留2位小数）
                            calculated_hours = round(time_diff.total_seconds() / 3600, 2)
                            if calculated_hours > 0:
                                update_fields["actual_hours"] = calculated_hours
                                logger.info(f"任务 {task_id} 状态更新为已完成，根据开始时间计算实际工时为 {calculated_hours} 小时")
                    else:
                        # 如果没有开始时间，使用创建时间计算
                        created_at = task.get("created_at")
                        if created_at:
                            current_time = datetime.utcnow()
                            if isinstance(created_at, datetime):
                                time_diff = current_time - created_at
                                calculated_hours = round(time_diff.total_seconds() / 3600, 2)
                                if calculated_hours > 0:
                                    update_fields["actual_hours"] = calculated_hours
                                    logger.info(f"任务 {task_id} 状态更新为已完成，根据创建时间计算实际工时为 {calculated_hours} 小时")
    except Exception as e:
        # 如果获取状态信息失败，记录错误但不影响状态更新
        logger.warning(f"获取状态信息失败: {str(e)}")
        pass
    
    # 更新任务状态
    await db.tasks.update_one(
        {"_id": object_id},
        {"$set": update_fields}
    )
    
    # 获取更新后的任务信息，用于返回给前端
    updated_task = await db.tasks.find_one({"_id": object_id})
    
    # 获取关联信息
    assignee_info = None
    reporter_info = None
    project_info = None
    creator_info = None
    
    if updated_task.get("assignee_id"):
        assignee_info = await db.users.find_one(
            {"_id": updated_task["assignee_id"]},
            {"_id": 1, "name": 1, "avatar": 1}
        )
    
    if updated_task.get("reporter_id"):
        reporter_info = await db.users.find_one(
            {"_id": updated_task["reporter_id"]},
            {"_id": 1, "name": 1, "avatar": 1}
        )
    
    if updated_task.get("created_by"):
        creator_info = await db.users.find_one(
            {"_id": updated_task["created_by"]},
            {"_id": 1, "name": 1, "avatar": 1}
        )
    
    if updated_task.get("project_id"):
        project_info = await db.projects.find_one(
            {"_id": updated_task["project_id"]},
            {"_id": 1, "name": 1}
        )
    
    # 获取任务关联的版本（取第一个）
    version_info = None
    task_relation = await db.version_task_relations.find_one(
        {"task_id": updated_task["_id"]}
    )
    if task_relation:
        version = await db.versions.find_one(
            {"_id": task_relation["version_id"]},
            {"_id": 1, "name": 1, "version_number": 1}
        )
        if version:
            version_info = version
    
    # 使用工具函数格式化响应，包含实际工时等信息
    task_data = format_task_response(
        task=updated_task,
        assignee_info=assignee_info,
        reporter_info=reporter_info,
        creator_info=creator_info,
        project_info=project_info,
        version_info=version_info,
        include_details=False  # 状态更新接口不需要详细信息
    )
    
    # 触发版本状态流转检查（异步，不阻塞响应）
    try:
        from app.utils.version_transition_trigger import trigger_version_transition_check
        import asyncio
        asyncio.create_task(trigger_version_transition_check(db, "task", object_id))
    except Exception as e:
        logger.warning(f"触发版本状态流转检查失败: {str(e)}")
    
    # 触发通知：任务状态变更时通知相关人员
    try:
        old_status = task.get("status")
        new_status = status_update.status
        
        # 如果状态发生变化，通知相关人员
        if old_status != new_status:
            # 获取状态名称
            state_doc = await db.states.find_one({
                "module_type": "task",
                "code": new_status
            })
            status_name = state_doc.get("name", new_status) if state_doc else new_status
            
            current_user_obj = await db.users.find_one({"_id": user_id})
            current_user_name = current_user_obj.get("name") or current_user_obj.get("username", "系统") if current_user_obj else "系统"
            
            # 通知任务创建者（如果不是当前用户）
            if task.get("created_by") and task["created_by"] != user_id:
                notification_data = NotificationCreate(
                    user_id=str(task["created_by"]),
                    type=NotificationType.TASK_STATUS_CHANGED,
                    title=f"任务状态更新：{task['title']}",
                    content=f"{current_user_name} 将任务「{task['title']}」的状态更新为「{status_name}」",
                    related_type=RelatedObjectType.TASK,
                    related_id=task_id
                )
                asyncio.create_task(
                    NotificationService.create_notification(db=db, notification_data=notification_data, send_email=False)
                )
            
            # 通知任务负责人（如果存在且不是当前用户和创建者）
            if task.get("assignee_id") and task["assignee_id"] != user_id and task["assignee_id"] != task.get("created_by"):
                notification_data = NotificationCreate(
                    user_id=str(task["assignee_id"]),
                    type=NotificationType.TASK_STATUS_CHANGED,
                    title=f"任务状态更新：{task['title']}",
                    content=f"{current_user_name} 将任务「{task['title']}」的状态更新为「{status_name}」",
                    related_type=RelatedObjectType.TASK,
                    related_id=task_id
                )
                asyncio.create_task(
                    NotificationService.create_notification(db=db, notification_data=notification_data, send_email=False)
                )
            
            # 如果状态变更为完成状态，发送完成通知 - 从状态管理系统动态判断
            validator = get_state_validator(db)
            try:
                state_info = await validator.get_state_info("task", new_status)
                if state_info:
                    state_name = state_info.get("name", "") if isinstance(state_info, dict) else getattr(state_info, "name", "")
                    state_name_lower = state_name.lower() if state_name else ""
                    # 检查状态名称是否包含"完成"、"已完成"等关键词
                    is_completed = any(keyword in state_name_lower for keyword in ["完成", "已完成", "完成状态"])
                    if is_completed and task.get("assignee_id") and task["assignee_id"] != user_id:
                        notification_data = NotificationCreate(
                            user_id=str(task["assignee_id"]),
                            type=NotificationType.TASK_COMPLETED,
                            title=f"任务完成：{task['title']}",
                            content=f"任务「{task['title']}」已完成",
                            related_type=RelatedObjectType.TASK,
                            related_id=task_id
                        )
                        asyncio.create_task(
                            NotificationService.create_notification(db=db, notification_data=notification_data, send_email=True)
                        )
            except Exception as e:
                logger.warning(f"判断任务完成状态失败: {str(e)}")
    except Exception as e:
        logger.warning(f"发送任务状态变更通知失败: {str(e)}")
    
    return success_response(data=task_data, message="更新任务状态成功")

@router.patch("/{task_id}/assignee", response_model=ApiResponse, summary="更新任务负责人")
async def update_task_assignee(
    task_id: str,
    assignee_update: TaskAssigneeUpdate,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    更新任务负责人
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid task ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            await PermissionService.require_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:assign"
            )
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=str(e) or "Not enough permissions to assign task"
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_permission = (
            Permissions.TASK_ASSIGN in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("created_by") == user_id
        )
        
        if not has_permission:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to assign task"
            )
    
    # 验证新负责人是否存在
    if assignee_update.assignee_id:
        try:
            assignee_object_id = ObjectId(assignee_update.assignee_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid assignee_id format"
            )
        
        assignee = await db.users.find_one({"_id": assignee_object_id})
        if not assignee:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Assignee not found"
            )
    else:
        assignee_object_id = None
    
    # 获取旧负责人
    old_assignee_id = task.get("assignee_id")
    
    # 更新任务负责人
    await db.tasks.update_one(
        {"_id": object_id},
        {
            "$set": {
                "assignee_id": assignee_object_id,
                "updated_at": datetime.utcnow()
            }
        }
    )
    
    # 触发通知：如果负责人发生变化，通知新负责人
    if assignee_object_id and assignee_object_id != old_assignee_id:
        try:
            # 通知新负责人
            if assignee_object_id != user_id:
                assignee = await db.users.find_one({"_id": assignee_object_id})
                current_user_obj = await db.users.find_one({"_id": user_id})
                current_user_name = current_user_obj.get("name") or current_user_obj.get("username", "系统") if current_user_obj else "系统"
                
                notification_data = NotificationCreate(
                    user_id=str(assignee_object_id),
                    type=NotificationType.TASK_ASSIGNED,
                    title=f"任务分配：{task['title']}",
                    content=f"{current_user_name} 将任务「{task['title']}」分配给了您",
                    related_type=RelatedObjectType.TASK,
                    related_id=task_id
                )
                asyncio.create_task(
                    NotificationService.create_notification(db=db, notification_data=notification_data, send_email=True)
                )
            
            # 如果旧负责人存在且不是当前用户，通知旧负责人任务已重新分配
            if old_assignee_id and old_assignee_id != user_id and old_assignee_id != assignee_object_id:
                current_user_obj = await db.users.find_one({"_id": user_id})
                current_user_name = current_user_obj.get("name") or current_user_obj.get("username", "系统") if current_user_obj else "系统"
                new_assignee_name = assignee.get("name") or assignee.get("username", "其他用户") if assignee else "其他用户"
                
                notification_data = NotificationCreate(
                    user_id=str(old_assignee_id),
                    type=NotificationType.TASK_UPDATED,
                    title=f"任务重新分配：{task['title']}",
                    content=f"{current_user_name} 将任务「{task['title']}」重新分配给了 {new_assignee_name}",
                    related_type=RelatedObjectType.TASK,
                    related_id=task_id
                )
                asyncio.create_task(
                    NotificationService.create_notification(db=db, notification_data=notification_data, send_email=False)
                )
        except Exception as e:
            logger.warning(f"发送任务分配通知失败: {str(e)}")
    
    return success_response(data={"id": task_id}, message="更新任务负责人成功")



@router.post("/{task_id}/time-logs", response_model=ApiResponse, summary="添加工时记录")
async def add_time_log(
    task_id: str,
    body: dict,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    添加工时记录（兼容 duration 和 hours 参数）
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid task ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            await PermissionService.require_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:log_time"
            )
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=str(e) or "Not enough permissions to log time for this task"
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_permission = (
            Permissions.TASK_LOG_TIME in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("assignee_id") == user_id
        )
        
        if not has_permission:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to log time for this task"
            )
    
    # 兼容 duration 和 hours 参数
    hours = body.get("hours") or body.get("duration", 0)
    description = body.get("description")
    date_str = body.get("date")
    
    if hours <= 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="hours or duration must be greater than 0"
        )
    
    # 创建工时记录
    time_log = {
        "id": ObjectId(),
        "hours": float(hours),
        "description": description,
        "logged_by": user_id,
        "logged_at": datetime.utcnow() if not date_str else datetime.fromisoformat(date_str.replace('Z', '+00:00'))
    }
    
    # 添加工时记录到任务
    await db.tasks.update_one(
        {"_id": object_id},
        {
            "$push": {"time_logs": time_log},
            "$set": {"updated_at": datetime.utcnow()}
        }
    )
    
    # 更新实际工时
    current_time_logs = task.get("time_logs", [])
    current_time_logs.append(time_log)
    total_actual_hours = sum(log.get("hours", 0) for log in current_time_logs)
    
    await db.tasks.update_one(
        {"_id": object_id},
        {"$set": {"actual_hours": total_actual_hours}}
    )
    
    return success_response(data={"id": str(time_log["id"])}, message="添加工时记录成功")

@router.get("/{task_id}/time-logs", response_model=None, summary="获取任务工时记录列表（分页）")
async def get_task_time_logs(
    task_id: str,
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(20, ge=1, le=200, description="每页数量"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取任务工时记录列表
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid task ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            has_access = await PermissionService.check_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:read"
            )
            if not has_access:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Not enough permissions to access this task"
                )
        except HTTPException:
            raise
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to access this task"
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_access = (
            Permissions.TASK_READ_ALL in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("assignee_id") == user_id or
            task.get("reporter_id") == user_id or
            task.get("created_by") == user_id
        )
        
        if not has_access:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to access this task"
            )
    
    # 获取工时记录
    time_logs = task.get("time_logs", [])
    
    # 按时间降序排序
    time_logs.sort(key=lambda x: x.get("logged_at", datetime.min), reverse=True)
    
    # 计算总数
    total = len(time_logs)
    
    # 分页
    start = (page - 1) * page_size
    end = start + page_size
    paginated_logs = time_logs[start:end]
    
    # 获取用户信息
    user_ids = set(log.get("logged_by") for log in paginated_logs if log.get("logged_by"))
    users_dict = {}
    if user_ids:
        users = await db.users.find(
            {"_id": {"$in": list(user_ids)}},
            {"_id": 1, "name": 1, "avatar": 1}
        ).to_list(length=None)
        users_dict = {str(user["_id"]): user for user in users}
    
    # 格式化工时记录
    formatted_logs = []
    for log in paginated_logs:
        user_info = users_dict.get(str(log.get("logged_by"))) if log.get("logged_by") else None
        formatted_logs.append({
            "id": str(log.get("id")),
            "hours": log.get("hours", 0),
            "description": log.get("description", ""),
            "logged_by": str(log.get("logged_by")) if log.get("logged_by") else None,
            "logged_by_name": user_info.get("name") if user_info else "Unknown",
            "logged_at": log.get("logged_at")
        })
    
    return paginated_response(
        items=formatted_logs,
        total=total,
        page=page,
        size=page_size,
        message="获取工时记录列表成功"
    )

@router.delete("/{task_id}/time-logs/{time_log_id}", response_model=ApiResponse, summary="删除任务工时记录")
async def delete_task_time_log(
    task_id: str,
    time_log_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    删除任务工时记录
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
        time_log_object_id = ObjectId(time_log_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            await PermissionService.require_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:update"
            )
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=str(e) or "Not enough permissions to delete this time log"
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        time_logs = task.get("time_logs", [])
        time_log = next((log for log in time_logs if log.get("id") == time_log_object_id), None)
        
        if not time_log:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Time log not found"
            )
        
        has_permission = (
            Permissions.TASK_UPDATE in user_permissions or
            Permissions.ADMIN in user_permissions or
            time_log.get("logged_by") == user_id
        )
        
        if not has_permission:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to delete this time log"
            )
    
    # 删除工时记录
    await db.tasks.update_one(
        {"_id": object_id},
        {
            "$pull": {"time_logs": {"id": time_log_object_id}},
            "$set": {"updated_at": datetime.utcnow()}
        }
    )
    
    # 重新计算实际工时
    remaining_logs = [log for log in time_logs if log.get("id") != time_log_object_id]
    total_actual_hours = sum(log.get("hours", 0) for log in remaining_logs)
    
    await db.tasks.update_one(
        {"_id": object_id},
        {"$set": {"actual_hours": total_actual_hours}}
    )
    
    return success_response(message="删除工时记录成功")

@router.patch("/{task_id}/progress", response_model=ApiResponse, summary="更新任务进度")
async def update_task_progress(
    task_id: str,
    progress: int = Query(..., ge=0, le=100, description="进度百分比（0-100）"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    更新任务进度
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid task ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            await PermissionService.require_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:update"
            )
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=str(e) or "Not enough permissions to update task progress"
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_permission = (
            Permissions.TASK_UPDATE in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("assignee_id") == user_id or
            task.get("reporter_id") == user_id or
            task.get("created_by") == user_id
        )
        
        if not has_permission:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to update task progress"
            )
    
    # 更新任务进度
    await db.tasks.update_one(
        {"_id": object_id},
        {
            "$set": {
                "progress": progress,
                "updated_at": datetime.utcnow()
            }
        }
    )
    
    return success_response(data={"id": task_id, "progress": progress}, message="更新任务进度成功")

@router.patch("/{task_id}/assign", response_model=ApiResponse, summary="更新任务负责人（兼容前端调用）")
async def assign_task(
    task_id: str,
    body: dict,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    更新任务负责人（兼容前端调用，内部调用 assignee 端点）
    """
    assignee_id = body.get("assignee_id")
    if not assignee_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="assignee_id is required"
        )
    
    assignee_update = TaskAssigneeUpdate(assignee_id=assignee_id)
    
    # 调用已有的 assignee 端点逻辑
    return await update_task_assignee(task_id, assignee_update, current_user, db)

@router.get("/board/{project_id}", response_model=ApiResponse, summary="获取项目任务看板")
async def get_task_board(
    project_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取项目任务看板
    """
    # 验证ObjectId格式
    try:
        project_object_id = ObjectId(project_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid project ID format"
        )
    
    # 检查项目是否存在
    project = await db.projects.find_one({"_id": project_object_id})
    if not project:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Project not found"
        )
    
    # 检查项目成员权限
    from app.services.permission_service import PermissionService
    try:
        project_id_str = str(project_object_id) if isinstance(project_object_id, ObjectId) else project_id
        has_access = await PermissionService.check_project_member_permission(
            project_id_str,
            current_user,
            db,
            "task:read"
        )
        if not has_access:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to access this project"
            )
    except HTTPException:
        raise
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions to access this project"
        )
    
    # 获取项目任务
    tasks = await db.tasks.find({"project_id": project_object_id}).to_list(length=None)
    
    # 从状态管理系统获取所有任务状态，动态构建看板结构
    from app.utils.state_utils import get_module_states
    task_states = await get_module_states(db, "task")
    
    # 按状态分组任务 - 动态构建看板结构（包含所有启用且可见的状态）
    board = {}
    for state in task_states:
        if state.is_enabled and state.is_visible:
            board[state.code] = []
    
    for task in tasks:
        task_summary = {
            "id": str(task["_id"]),
            "title": task["title"],
            "description": task.get("description"),
            "task_type": task["type"],  # 修复字段名
            "priority": task["priority"],
            "assignee_id": str(task["assignee_id"]) if task.get("assignee_id") else None,
            "estimated_hours": task.get("estimated_hours"),
            "due_date": task.get("due_date"),
            "tags": task.get("tags", []),
            "created_at": task["created_at"],
            "updated_at": task["updated_at"]
        }
        
        status = task.get("status", "")
        if status and status in board:
            board[status].append(task_summary)
        elif status and status not in board:
            # 如果状态不在看板中，动态添加该状态列
            board[status] = [task_summary]
    
    return success_response(data=board, message="获取任务看板成功")

@router.get("/stats/overview", response_model=ApiResponse, summary="获取任务统计概览")
async def get_task_stats(
    project_id: Optional[str] = Query(None, description="项目ID筛选"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取任务统计概览
    """
    # 构建查询条件
    query = {}
    
    # 权限控制
    user_permissions = current_user.get("permissions", [])
    user_id = ObjectId(current_user.get("user_id"))
    
    if Permissions.TASK_READ_ALL not in user_permissions and Permissions.ADMIN not in user_permissions:
        user_projects = await db.projects.find({
            "$or": [
                {"is_public": True},
                {"created_by": user_id},
                {"members.user_id": user_id}
            ]
        }, {"_id": 1}).to_list(length=None)
        
        project_ids = [project["_id"] for project in user_projects]
        
        query["$or"] = [
            {"assignee_id": user_id},
            {"reporter_id": user_id},
            {"project_id": {"$in": project_ids}}
        ]
    
    if project_id:
        try:
            query["project_id"] = ObjectId(project_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid project_id format"
            )
    
    # 统计任务总数
    total_tasks = await db.tasks.count_documents(query)
    
    # 按状态分组统计
    pipeline = [
        {"$match": query},
        {"$group": {"_id": "$status", "count": {"$sum": 1}}}
    ]
    status_stats = await db.tasks.aggregate(pipeline).to_list(length=None)
    tasks_by_status = {stat["_id"]: stat["count"] for stat in status_stats}
    
    # 构建统计数据格式 - 包含所有状态的统计
    stats_data = {
        "total": total_tasks,
        "tasks_by_status": tasks_by_status  # 所有状态的统计，前端应从状态管理系统动态获取状态信息
    }
    
    return success_response(data=stats_data, message="获取任务统计成功")

@router.delete("/batch", response_model=ApiResponse, summary="批量删除任务")
async def batch_delete_tasks(
    ids: List[str] = Query(..., description="任务ID列表"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    批量删除任务
    """
    # 权限检查
    user_permissions = current_user.get("permissions", [])
    user_id = ObjectId(current_user.get("user_id"))
    
    if Permissions.TASK_DELETE not in user_permissions and Permissions.ADMIN not in user_permissions:
        return error_response(
            error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
            message="Not enough permissions to delete tasks",
            code=403
        )
    
    # 验证ObjectId格式
    object_ids = []
    invalid_ids = []
    for task_id in ids:
        try:
            object_ids.append(ObjectId(task_id))
        except Exception:
            invalid_ids.append(task_id)
    
    if invalid_ids:
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message=f"Invalid task IDs: {', '.join(invalid_ids)}",
            code=400
        )
    
    if not object_ids:
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="No valid task IDs provided",
            code=400
        )
    
    # 查询任务并检查权限
    tasks = await db.tasks.find({"_id": {"$in": object_ids}}).to_list(length=None)
    
    if not tasks:
        return error_response(
            error_type=ErrorTypes.NOT_FOUND,
            message="No tasks found",
            code=404
        )
    
    # 对于非管理员，检查每个任务的权限
    if Permissions.ADMIN not in user_permissions:
        deletable_ids = []
        for task in tasks:
            # 检查是否有权限删除此任务
            can_delete = (
                task.get("created_by") == user_id or
                task.get("assignee_id") == user_id or
                task.get("reporter_id") == user_id
            )
            
            # 检查项目权限
            if not can_delete and task.get("project_id"):
                project = await db.projects.find_one({"_id": task["project_id"]})
                if project:
                    can_delete = (
                        project.get("created_by") == user_id or
                        any(member["user_id"] == user_id and member.get("role") in ["admin", "manager"]
                            for member in project.get("members", []))
                    )
    
            if can_delete:
                deletable_ids.append(task["_id"])
        
        if not deletable_ids:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message="Not enough permissions to delete any of the specified tasks",
                code=403
            )
        
        object_ids = deletable_ids
    
    # 批量删除任务
    result = await db.tasks.delete_many({"_id": {"$in": object_ids}})
    
    return success_response(
        data={"deleted_count": result.deleted_count},
        message=f"成功删除 {result.deleted_count} 个任务"
    )

@router.put("/batch", response_model=ApiResponse, summary="批量更新任务")
async def batch_update_tasks(
    body: dict = Body(...),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    批量更新任务
    """
    ids = body.get("ids", [])
    
    if isinstance(ids, str):
        ids = [ids.strip()]
    elif isinstance(ids, (set, tuple)):
        ids = [str(i).strip() for i in ids]
    elif not isinstance(ids, list):
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="Invalid ids format, expected array of task IDs",
            code=400
        )
    else:
        ids = [str(i).strip() for i in ids if i is not None]
    updates = {k: v for k, v in body.items() if k != "ids" and v is not None}
    
    if not ids:
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="Task IDs are required",
            code=400
        )
    
    if not updates:
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="No update fields provided",
            code=400
        )
    
    # 权限检查
    user_permissions = current_user.get("permissions", [])
    user_id = ObjectId(current_user.get("user_id"))
    
    # 判断是否为分配操作（只更新 assignee_id）
    is_assign_operation = "assignee_id" in updates and len(updates) == 1
    
    # 根据操作类型检查基础权限
    if is_assign_operation:
        # 分配操作需要 TASK_ASSIGN 权限
        if Permissions.TASK_ASSIGN not in user_permissions and Permissions.ADMIN not in user_permissions:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message="Not enough permissions to assign tasks",
                code=403
            )
    else:
        # 其他更新操作需要 TASK_UPDATE 权限
        if Permissions.TASK_UPDATE not in user_permissions and Permissions.ADMIN not in user_permissions:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message="Not enough permissions to update tasks",
                code=403
            )
    
    # 验证ObjectId格式
    object_ids: List[ObjectId] = []
    invalid_ids: List[str] = []
    for task_id in ids:
        if not task_id:
            continue
        if ObjectId.is_valid(task_id):
            object_ids.append(ObjectId(task_id))
        else:
            invalid_ids.append(task_id)
    
    if invalid_ids:
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="Invalid task ID format",
            details={"invalid_ids": invalid_ids},
            code=400
        )
    
    # 查询任务
    tasks = await db.tasks.find({"_id": {"$in": object_ids}}).to_list(length=None)
    
    if len(tasks) != len(object_ids):
        return error_response(
            error_type=ErrorTypes.NOT_FOUND,
            message="Some tasks not found",
            code=404
        )
    
    # 对于非管理员，检查每个任务的权限
    if Permissions.ADMIN not in user_permissions:
        updatable_ids = []
        for task in tasks:
            if is_assign_operation:
                # 分配操作：检查分配权限
                can_assign = (
                    Permissions.TASK_ASSIGN in user_permissions or
                    task.get("created_by") == user_id
                )
                
                # 检查项目权限
                if not can_assign and task.get("project_id"):
                    project = await db.projects.find_one({"_id": task["project_id"]})
                    if project:
                        can_assign = (
                            project["created_by"] == user_id or
                            any(
                                member["user_id"] == user_id and "task:assign" in member.get("permissions", [])
                                for member in project.get("members", [])
                            )
                        )
                
                if can_assign:
                    updatable_ids.append(task["_id"])
            else:
                # 其他更新操作：检查更新权限
                can_update = (
                    Permissions.TASK_UPDATE in user_permissions or
                    task.get("assignee_id") == user_id or
                    task.get("reporter_id") == user_id or
                    task.get("created_by") == user_id
                )
                
                # 检查项目权限
                if not can_update and task.get("project_id"):
                    project = await db.projects.find_one({"_id": task["project_id"]})
                    if project:
                        can_update = (
                            project["created_by"] == user_id or
                            any(
                                member["user_id"] == user_id and "task:update" in member.get("permissions", [])
                                for member in project.get("members", [])
                            )
                        )
                
                if can_update:
                    updatable_ids.append(task["_id"])
        
        if not updatable_ids:
            operation_type = "分配" if is_assign_operation else "更新"
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message=f"Not enough permissions to {operation_type} any of the specified tasks",
                code=403
            )
        
        object_ids = updatable_ids
    
    # 构建更新数据
    update_data = {"updated_at": datetime.utcnow()}
    
    # 处理状态更新
    if "status" in updates:
        validator = get_state_validator(db)
        is_valid = await validator.validate_state_code("task", updates["status"])
        if not is_valid:
            return error_response(
                error_type=ErrorTypes.INVALID_PARAMETER,
                message=f"Invalid status value: {updates['status']}",
                code=400
            )
        update_data["status"] = updates["status"]
        
        # 检查状态是否为已完成状态，如果是则自动设置进度为100并计算实际工时
        try:
            state_info = await validator.get_state_info("task", updates["status"])
            if state_info:
                state_name = ""
                state_description = ""
                if hasattr(state_info, "name"):
                    state_name = getattr(state_info, "name", "")
                    state_description = getattr(state_info, "description", "") or ""
                elif isinstance(state_info, dict):
                    state_name = state_info.get("name", "")
                    state_description = state_info.get("description", "") or ""
                
                completed_keywords = ["已完成", "完成", "完成状态"]
                is_completed = any(keyword in state_name or keyword in state_description 
                                 for keyword in completed_keywords)
                
                if is_completed:
                    update_data["progress"] = 100
                    logger.info(f"批量更新：状态 {updates['status']} ({state_name}) 被识别为已完成状态，自动设置进度为100%")
                    
                    # 为每个任务计算实际工时（如果还未设置）
                    current_time = datetime.utcnow()
                    for task in tasks:
                        current_actual_hours = task.get("actual_hours")
                        time_logs = task.get("time_logs", [])
                        
                        # 如果实际工时未设置或为0，且没有工时记录，则根据开始时间计算
                        if (current_actual_hours is None or current_actual_hours == 0) and not time_logs:
                            start_date = task.get("start_date")
                            if start_date and isinstance(start_date, datetime):
                                time_diff = current_time - start_date
                                calculated_hours = round(time_diff.total_seconds() / 3600, 2)
                                if calculated_hours > 0:
                                    # 单独更新该任务的实际工时
                                    await db.tasks.update_one(
                                        {"_id": task["_id"]},
                                        {"$set": {"actual_hours": calculated_hours}}
                                    )
                                    logger.info(f"批量更新：任务 {task['_id']} 状态更新为已完成，根据开始时间计算实际工时为 {calculated_hours} 小时")
                            else:
                                # 如果没有开始时间，使用创建时间计算
                                created_at = task.get("created_at")
                                if created_at and isinstance(created_at, datetime):
                                    time_diff = current_time - created_at
                                    calculated_hours = round(time_diff.total_seconds() / 3600, 2)
                                    if calculated_hours > 0:
                                        await db.tasks.update_one(
                                            {"_id": task["_id"]},
                                            {"$set": {"actual_hours": calculated_hours}}
                                        )
                                        logger.info(f"批量更新：任务 {task['_id']} 状态更新为已完成，根据创建时间计算实际工时为 {calculated_hours} 小时")
        except Exception as e:
            logger.warning(f"批量更新：获取状态信息失败: {str(e)}")
            pass
    
    # 处理负责人更新
    if "assignee_id" in updates:
        try:
            assignee_id = ObjectId(updates["assignee_id"]) if updates["assignee_id"] else None
            if assignee_id:
                assignee = await db.users.find_one({"_id": assignee_id})
                if not assignee:
                    return error_response(
                        error_type=ErrorTypes.NOT_FOUND,
                        message="Assignee not found",
                        code=404
                    )
            update_data["assignee_id"] = assignee_id
        except Exception:
            return error_response(
                error_type=ErrorTypes.INVALID_PARAMETER,
                message="Invalid assignee_id format",
                code=400
            )
    
    # 处理优先级更新
    if "priority" in updates:
        update_data["priority"] = updates["priority"]
    
    # 批量更新任务
    result = await db.tasks.update_many(
        {"_id": {"$in": object_ids}},
        {"$set": update_data}
    )
    
    return success_response(
        data={"updated_count": result.modified_count},
        message=f"成功更新 {result.modified_count} 个任务"
    )

@router.get("/{task_id}/attachments", response_model=ApiResponse, summary="获取任务附件列表")
async def get_task_attachments(
    task_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取任务附件列表
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="Invalid task ID format",
            code=400
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        return error_response(
            error_type=ErrorTypes.NOT_FOUND,
            message="Task not found",
            code=404
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            has_access = await PermissionService.check_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:read"
            )
            if not has_access:
                return error_response(
                    error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                    message="Not enough permissions to access this task",
                    code=403
                )
        except Exception:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message="Not enough permissions to access this task",
                code=403
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_access = (
            Permissions.TASK_READ_ALL in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("assignee_id") == user_id or
            task.get("reporter_id") == user_id or
            task.get("created_by") == user_id
        )
        
        if not has_access:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message="Not enough permissions to access this task",
                code=403
            )
    
    # 获取附件列表
    attachments = task.get("attachments", [])
    
    # 格式化附件信息
    formatted_attachments = []
    for attachment in attachments:
        uploaded_at = attachment.get("uploaded_at")
        # 统一时间格式为ISO字符串
        if isinstance(uploaded_at, datetime):
            uploaded_at_str = uploaded_at.isoformat()
        elif uploaded_at:
            uploaded_at_str = str(uploaded_at)
        else:
            uploaded_at_str = None
        
        formatted_attachments.append({
            "id": str(attachment.get("id")),
            "filename": attachment.get("filename", ""),
            "original_filename": attachment.get("original_filename", ""),
            "file_size": attachment.get("file_size", 0),
            "file_type": attachment.get("file_type", ""),
            "url": attachment.get("url", ""),
            "uploaded_by": str(attachment.get("uploaded_by")) if attachment.get("uploaded_by") else None,
            "uploaded_at": uploaded_at_str
        })
    
    return success_response(
        data=formatted_attachments,
        message="获取附件列表成功"
    )

@router.post("/{task_id}/attachments", response_model=ApiResponse, summary="上传任务附件")
async def upload_task_attachment(
    task_id: str,
    file: UploadFile = File(...),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    上传任务附件
    """
    # 验证ObjectId格式
    try:
        object_id = ObjectId(task_id)
    except Exception:
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="Invalid task ID format",
            code=400
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": object_id})
    if not task:
        return error_response(
            error_type=ErrorTypes.NOT_FOUND,
            message="Task not found",
            code=404
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            await PermissionService.require_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:update"
            )
        except Exception as e:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message=str(e) or "Not enough permissions to upload attachments for this task",
                code=403
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_permission = (
            Permissions.TASK_UPDATE in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("assignee_id") == user_id or
            task.get("reporter_id") == user_id or
            task.get("created_by") == user_id
        )
        
        if not has_permission:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message="Not enough permissions to upload attachments for this task",
                code=403
            )
    
    try:
        attachment_meta = await attachment_service.upload(
            file=file,
            directory=f"tasks/{task_id}",
            filename_prefix=f"task_{task_id}",
            max_size=getattr(settings, 'MAX_FILE_SIZE', 50 * 1024 * 1024)
        )
    except ValueError as exc:
        return error_response(
            error_type=ErrorTypes.VALIDATION_ERROR,
            message=str(exc),
            code=400
        )
    except Exception as exc:
        return error_response(
            error_type=ErrorTypes.SERVER_ERROR,
            message=f"Failed to upload file to storage: {str(exc)}",
            code=500
        )

    attachment = {
        "id": ObjectId(),
        **attachment_meta,
        "url": f"/api/v1/tasks/{task_id}/attachments/{attachment_meta['filename']}/download",
        "uploaded_by": user_id
    }

    current_attachments = task.get("attachments", [])
    attachment_count = len(current_attachments)

    await db.tasks.update_one(
        {"_id": object_id},
        {
            "$push": {"attachments": attachment},
            "$set": {
                "updated_at": datetime.utcnow(),
                "attachment_count": attachment_count + 1
            }
        }
    )

    uploaded_at = attachment["uploaded_at"]
    uploaded_at_iso = uploaded_at.isoformat() if isinstance(uploaded_at, datetime) else uploaded_at

    return success_response(
        data={
            "id": str(attachment["id"]),
            "filename": attachment["filename"],
            "original_filename": attachment["original_filename"],
            "file_size": attachment["file_size"],
            "file_type": attachment["file_type"],
            "url": attachment["url"],
            "uploaded_by": str(attachment["uploaded_by"]),
            "uploaded_at": uploaded_at_iso
        },
        message="上传附件成功"
    )

@router.delete("/{task_id}/attachments/{attachment_id}", response_model=ApiResponse, summary="删除任务附件")
async def delete_task_attachment(
    task_id: str,
    attachment_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    删除任务附件
    """
    # 验证ObjectId格式
    try:
        task_object_id = ObjectId(task_id)
        attachment_object_id = ObjectId(attachment_id)
    except Exception:
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="Invalid ID format",
            code=400
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": task_object_id})
    if not task:
        return error_response(
            error_type=ErrorTypes.NOT_FOUND,
            message="Task not found",
            code=404
        )
    
    # 查找附件
    attachments = task.get("attachments", [])
    attachment = next((a for a in attachments if a.get("id") == attachment_object_id), None)
    
    if not attachment:
        return error_response(
            error_type=ErrorTypes.NOT_FOUND,
            message="Attachment not found",
            code=404
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            await PermissionService.require_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:update"
            )
        except Exception as e:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message=str(e) or "Not enough permissions to delete this attachment",
                code=403
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_permission = (
            Permissions.TASK_UPDATE in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("created_by") == user_id or
            attachment.get("uploaded_by") == user_id
        )
        
        if not has_permission:
            return error_response(
                error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
                message="Not enough permissions to delete this attachment",
                code=403
            )
    
    filename = attachment.get("filename", "")
    if filename:
        attachment_service.delete(directory=f"tasks/{task_id}", filename=filename)

    try:
        current_attachments = task.get("attachments", [])
        attachment_count = len(current_attachments) - 1

        await db.tasks.update_one(
            {"_id": task_object_id},
            {
                "$pull": {"attachments": {"id": attachment_object_id}},
                "$set": {
                    "updated_at": datetime.utcnow(),
                    "attachment_count": max(0, attachment_count)
                }
            }
        )

        return success_response(message="删除附件成功")
    except Exception as e:
        return error_response(
            error_type=ErrorTypes.SERVER_ERROR,
            message=f"Failed to delete attachment: {str(e)}",
            code=500
        )

@router.get("/{task_id}/attachments/{filename}/download", summary="下载任务附件")
async def download_task_attachment(
    task_id: str,
    filename: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    下载任务附件
    """
    # 验证ObjectId格式
    try:
        task_object_id = ObjectId(task_id)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid task ID format"
        )
    
    # 检查任务是否存在
    task = await db.tasks.find_one({"_id": task_object_id})
    if not task:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Task not found"
        )
    
    # 查找附件
    attachments = task.get("attachments", [])
    attachment = next((a for a in attachments if a.get("filename") == filename), None)
    
    if not attachment:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Attachment not found"
        )
    
    # 检查项目成员权限
    project_id = task.get("project_id")
    if project_id:
        from app.services.permission_service import PermissionService
        try:
            project_id_str = str(project_id) if isinstance(project_id, ObjectId) else project_id
            has_access = await PermissionService.check_project_member_permission(
                project_id_str,
                current_user,
                db,
                "task:read"
            )
            if not has_access:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Not enough permissions to access this attachment"
                )
        except HTTPException:
            raise
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to access this attachment"
            )
    else:
        # 如果没有项目ID，使用旧的权限检查（向后兼容）
        user_permissions = current_user.get("permissions", [])
        user_id = ObjectId(current_user.get("user_id"))
        
        has_access = (
            Permissions.TASK_READ_ALL in user_permissions or
            Permissions.ADMIN in user_permissions or
            task.get("assignee_id") == user_id or
            task.get("reporter_id") == user_id or
            task.get("created_by") == user_id
        )
        
        if not has_access:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions to access this attachment"
            )
    
    try:
        file_content, content_type = attachment_service.download(
            directory=f"tasks/{task_id}",
            filename=filename
        )
    except FileNotFoundError:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Attachment not found"
        )
    except Exception as exc:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to download file: {str(exc)}"
        )

    download_filename = attachment.get("original_filename", filename)
    try:
        download_filename.encode('ascii')
        content_disposition = f"attachment; filename=\"{download_filename}\""
    except UnicodeEncodeError:
        encoded_filename = quote(download_filename.encode('utf-8'), safe='')
        ascii_fallback = filename
        content_disposition = f"attachment; filename=\"{ascii_fallback}\"; filename*=UTF-8''{encoded_filename}"

    return StreamingResponse(
        io.BytesIO(file_content),
        media_type=content_type or attachment.get("file_type", "application/octet-stream"),
        headers={"Content-Disposition": content_disposition}
    )

# 注意：/export 路由已在上方定义（第455行），这里删除重复定义以避免路由冲突

@router.post("/import", response_model=ApiResponse, summary="导入任务（Excel格式）")
async def import_tasks(
    file: UploadFile = File(...),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    从Excel文件导入任务数据
    """
    # 检查权限
    user_permissions = current_user.get("permissions", [])
    if Permissions.TASK_CREATE not in user_permissions and Permissions.ADMIN not in user_permissions:
        return error_response(
            error_type=ErrorTypes.PERMISSION_DENIED_ERROR,
            message="Not enough permissions to import tasks",
            code=403
        )
    
    # 验证文件类型
    if not file.filename or not file.filename.endswith(('.xlsx', '.xls')):
        return error_response(
            error_type=ErrorTypes.INVALID_PARAMETER,
            message="只支持 Excel 文件 (.xlsx, .xls)",
            code=400
        )
    
    user_id = ObjectId(current_user.get("user_id"))
    success_count = 0
    error_count = 0
    errors = []
    
    try:
        # 读取文件内容
        content = await file.read()
        
        # 使用openpyxl读取Excel
        wb = load_workbook(io.BytesIO(content), data_only=True)
        ws = wb.active
        
        # 读取表头（第一行）
        headers = []
        for cell in ws[1]:
            headers.append(cell.value or "")
        
        # 验证必需的列
        required_columns = ["标题", "项目"]
        missing_columns = [col for col in required_columns if col not in headers]
        if missing_columns:
            return error_response(
                error_type=ErrorTypes.INVALID_PARAMETER,
                message=f"缺少必需的列: {', '.join(missing_columns)}",
                code=400
            )
        
        # 获取列索引映射
        col_map = {header: idx + 1 for idx, header in enumerate(headers)}
        
        # 获取状态验证器
        validator = get_state_validator(db)
        
        # 处理每一行数据（从第二行开始）
        for row_idx, row in enumerate(ws.iter_rows(min_row=2, values_only=False), start=2):
            try:
                # 跳过空行
                if not any(cell.value for cell in row):
                    continue
                
                # 读取数据
                row_data = {}
                for header, col_idx in col_map.items():
                    cell_value = row[col_idx - 1].value if col_idx <= len(row) else None
                    if cell_value is not None:
                        row_data[header] = cell_value
                
                # 提取必需字段
                title = str(row_data.get("标题", "")).strip()
                if not title:
                    error_count += 1
                    errors.append(f"第{row_idx}行: 标题不能为空")
                    continue
                
                project_name = str(row_data.get("项目", "")).strip()
                if not project_name:
                    error_count += 1
                    errors.append(f"第{row_idx}行: 项目不能为空")
                    continue
                
                # 查找项目
                project = await db.projects.find_one({"name": project_name})
                if not project:
                    error_count += 1
                    errors.append(f"第{row_idx}行: 项目 '{project_name}' 不存在")
                    continue
                
                # 检查项目权限
                has_project_access = (
                    Permissions.PROJECT_READ_ALL in user_permissions or
                    Permissions.ADMIN in user_permissions or
                    project["is_public"] or
                    project["created_by"] == user_id or
                    any(member["user_id"] == user_id for member in project.get("members", []))
                )
                
                if not has_project_access:
                    error_count += 1
                    errors.append(f"第{row_idx}行: 没有权限在项目 '{project_name}' 中创建任务")
                    continue
                
                # 提取可选字段
                description = str(row_data.get("描述", "")).strip() or None
                task_type = str(row_data.get("任务类型", "")).strip() or "feature"
                # 从状态管理系统获取默认状态
                from app.utils.state_utils import get_default_state_code
                default_status = await get_default_state_code(db, "task")
                status = str(row_data.get("状态", "")).strip() or default_status or ""
                priority = str(row_data.get("优先级", "")).strip() or "medium"
                
                # 验证状态
                is_valid_status = await validator.validate_state_code("task", status)
                if not is_valid_status:
                    # 使用默认状态
                    status = default_status or ""
                
                # 查找负责人
                assignee_id = None
                assignee_name = str(row_data.get("负责人", "")).strip()
                if assignee_name:
                    assignee = await db.users.find_one({"name": assignee_name})
                    if assignee:
                        assignee_id = assignee["_id"]
                    else:
                        error_count += 1
                        errors.append(f"第{row_idx}行: 负责人 '{assignee_name}' 不存在")
                        continue
                
                # 查找报告人（默认为当前用户）
                reporter_id = user_id
                reporter_name = str(row_data.get("报告人", "")).strip()
                if reporter_name:
                    reporter = await db.users.find_one({"name": reporter_name})
                    if reporter:
                        reporter_id = reporter["_id"]
                
                # 解析日期
                start_date = None
                due_date = None
                
                start_date_str = str(row_data.get("开始日期", "")).strip()
                if start_date_str:
                    try:
                        start_date = datetime.strptime(start_date_str, "%Y-%m-%d")
                    except:
                        try:
                            start_date = datetime.fromisoformat(start_date_str.replace('Z', '+00:00'))
                        except:
                            pass
                
                due_date_str = str(row_data.get("截止日期", "")).strip()
                if due_date_str:
                    try:
                        due_date = datetime.strptime(due_date_str, "%Y-%m-%d")
                    except:
                        try:
                            due_date = datetime.fromisoformat(due_date_str.replace('Z', '+00:00'))
                        except:
                            pass
                
                # 解析数字字段
                progress = None
                progress_str = str(row_data.get("进度(%)", "")).strip()
                if progress_str:
                    try:
                        progress = int(float(progress_str))
                        progress = max(0, min(100, progress))
                    except:
                        pass
                
                estimated_hours = None
                estimated_hours_str = str(row_data.get("预估工时", "")).strip()
                if estimated_hours_str:
                    try:
                        estimated_hours = float(estimated_hours_str)
                        estimated_hours = max(0, estimated_hours)
                    except:
                        pass
                
                actual_hours = None
                actual_hours_str = str(row_data.get("实际工时", "")).strip()
                if actual_hours_str:
                    try:
                        actual_hours = float(actual_hours_str)
                        actual_hours = max(0, actual_hours)
                    except:
                        pass
                
                # 解析标签
                tags = []
                tags_str = str(row_data.get("标签", "")).strip()
                if tags_str:
                    tags = [tag.strip() for tag in tags_str.split(",") if tag.strip()]
                
                # 创建任务文档
                task_doc = {
                    "title": title,
                    "description": description,
                    "type": task_type,
                    "status": status,
                    "priority": priority,
                    "project_id": project["_id"],
                    "assignee_id": assignee_id,
                    "reporter_id": reporter_id,
                    "created_by": user_id,
                    "start_date": start_date,
                    "due_date": due_date,
                    "progress": progress,
                    "estimated_hours": estimated_hours,
                    "actual_hours": actual_hours,
                    "tags": tags,
                    "comments": [],
                    "attachments": [],
                    "time_logs": [],
                    "created_at": datetime.utcnow(),
                    "updated_at": datetime.utcnow()
                }
                
                # 插入数据库
                await db.tasks.insert_one(task_doc)
                success_count += 1
                
            except Exception as e:
                error_count += 1
                error_msg = f"第{row_idx}行: {str(e)}"
                errors.append(error_msg)
                import logging
                logger = logging.getLogger(__name__)
                logger.error(f"导入任务失败: {error_msg}", exc_info=True)
        
        # 返回结果
        message = f"导入完成: 成功 {success_count} 条"
        if error_count > 0:
            message += f", 失败 {error_count} 条"
        
        return success_response(
            data={
                "success_count": success_count,
                "error_count": error_count,
                "errors": errors[:50]  # 最多返回50个错误
            },
            message=message
        )
        
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f"导入任务文件失败: {str(e)}", exc_info=True)
        return error_response(
            error_type=ErrorTypes.INTERNAL_SERVER_ERROR,
            message=f"导入文件失败: {str(e)}",
            code=500
        )

# 注意：/import/template 路由已在上方定义（第695行），这里删除重复定义以避免路由冲突